#include "worker.h"
#include "taskQueue.h"
#include "ThreadPool.h"
#include "mysqlFunc.h"
#include "transFile.h"
#include "clientTask.h"

#define SOCKET_NUM 1024

int exitPipe[2];
void handler(int signum){
    printf("signum = %d\n", signum);
    write(exitPipe[1], "1", 1);
}

int main(int argc, char **argv){
    // ./server 192.168.0.69 1234 4
    ARGS_CHECK(argc, 4);
    pipe(exitPipe);
    if(0 != fork()){
        //父进程
        close(exitPipe[0]);
        signal(SIGUSR1, handler);
        wait(NULL);
        printf("parent process is going to exit!\n");
        exit(0);
    }
    //子进程-主线程
    close(exitPipe[1]);
    threadPool_t threadPool;
    threadPoolInit(&threadPool, atoi(argv[3]));
    makeWorker(&threadPool);

    MYSQL *sqlconn = NULL;
    mysqlInit(&sqlconn);

    int sockFd;
    tcpInit(&sockFd, argv[1], argv[2]);
    int epfd = epoll_create(1);
    epollAdd(epfd, exitPipe[0]);
    epollAdd(epfd, sockFd);
    struct epoll_event readySet[SOCKET_NUM];
    connectUser_t cUser[SOCKET_NUM];
    for(int k = 0; k < SOCKET_NUM; ++k){
        bzero(&cUser[k], sizeof(connectUser_t));
    }
    train_t train;
    char buf[10];
    int i = 0, j = 0, netFd = -1, ret = -1;

    char cmdStr[100];
    char *cmdStrArr[] = {0};
    int cmdStrNum = 0;
    int queryResult = 0;//查询结果, 查询到了=1, 没查询到=2
    fileTable_t fileTable;//保存文件树表查询结果
    dirStack_t dirStack[SOCKET_NUM];//存储用户路径
    dirStack_t tmpDirStack;//临时用户路径
    for(int k = 0; k < SOCKET_NUM; ++k){
        bzero(&dirStack[k], sizeof(dirStack_t));
    }
    char* cdStrArr[] = {0};
    char cdTmpDir[200] = {0};
    int cdStrNum = 0;

    while(1){
        int readyNum = epoll_wait(epfd, readySet, SOCKET_NUM, -1);
        for(i = 0; i < readyNum; ++i){
            if(readySet[i].events == EPOLLIN && readySet[i].data.fd == sockFd){
                //客户端连接请求
                for(j = 0; j < SOCKET_NUM; ++j){
                    if(0 == cUser[j].netFd){
                        break;
                    } 
                }
                if(j == SOCKET_NUM){
                    //连接满了或者为旧连接
                    continue;
                }
                //加入用户队列
                cUser[j].netFd = accept(sockFd, NULL, NULL);//client connect
                cUser[j].state = 1;
                //dirStackInit(&dirStack[j]);//用户目录初始化
                dirStack[j].top = -1;
                char dbuf[1] = "/";
                memcpy(dirStack[j].dir, dbuf, 1);
                printf("dirStack[%d].top = %d\n", j, dirStack[j].top);
                printf("dirStack[%d].dir = %s\n", j, dirStack[j].dir);
                memcpy(cUser[j].clientPath, dirStack[j].dir, strlen(dirStack[j].dir));
                epollAdd(epfd, cUser[j].netFd);
                printf("client [%d] is connect!\n", j);
            }
            else if(readySet[i].events == EPOLLIN && readySet[i].data.fd == exitPipe[0]){
                //主进程通知退出
                //1、先退出子线程
                printf("child thread is going to exit!\n");
                pthread_mutex_lock(&threadPool.mutex);
                threadPool.exitFlag = 1;
                pthread_cond_broadcast(&threadPool.cond);
                pthread_mutex_unlock(&threadPool.mutex);
                for(int j = 0; j < threadPool.tidArr.workerNum; ++j){
                    pthread_join(threadPool.tidArr.arr[j], NULL);
                }
                //2、再退出主线程
                printf("master thread is going to exit!\n");
                mysql_close(sqlconn);
                exit(0);
            }

            for(j = 0; j < SOCKET_NUM; ++j){
                if(readySet[i].events == EPOLLIN && readySet[i].data.fd == cUser[j].netFd){
                    bzero(&train, sizeof(train_t));
                    recv_once(cUser[j].netFd, &train, sizeof(train_t));//接收任务类型
                    printf("收到客户端[%d]的消息%d: %s\n", j, cUser[j].netFd, train.data);
                    parseStr(train.data, cmdStrArr, " ", &cmdStrNum, &train);
                    printf("train.code = %d\n", train.code);
                    if(32 == train.code || CLOSE_CONNECT == train.code){
                        //客户端断开连接
                        //1、将该客户端的netFd关闭, 从epdf中删除
                        epollDel(epfd, cUser[j].netFd);
                        close(cUser[j].netFd);
                        //2、netFd移除任务队列
                        printf("断开客户端 [%d] 的连接!\n", cUser[j].netFd);
                        bzero(&cUser[j], sizeof(connectUser_t));
                        break;
                    }
                    else if(REGISTER_APPLY == train.code){
                        //告诉客户端已连接成功
                        ret = send(cUser[j].netFd, &train, sizeof(train_t), MSG_NOSIGNAL);
                        ERROR_CHECK(ret, -1, "send");
                        printf("连接成功已发送客户端\n");
                        
                        recv_once(cUser[j].netFd, &train, sizeof(train_t));//接收用户账号
                        printf("userName = %s\n", train.data);
                        memcpy(cUser[j].userName, train.data, train.length);
                        
                        recv_once(cUser[j].netFd, &train, sizeof(train_t));//接收用户密码
                        printf("userPasswd = %s\n", train.data);
                        memcpy(cUser[j].userPasswd, train.data, train.length);
                    }
                    else if(LOGIN_APPLY == train.code){
                        //登录
                        ret = send(cUser[j].netFd, &train, sizeof(train_t), MSG_NOSIGNAL);
                        ERROR_CHECK(ret, -1, "send");
                        printf("连接成功已发送客户端\n");
                        
                        recv_once(cUser[j].netFd, &train, sizeof(train_t));//接收用户账号
                        printf("userName = %s\n", train.data);
                        memcpy(cUser[j].userName, train.data, train.length);
                        
                        recv_once(cUser[j].netFd, &train, sizeof(train_t));//接收用户密码
                        printf("userPasswd = %s\n", train.data);
                        memcpy(cUser[j].userPasswd, train.data, train.length);
                    }
                    else if(LS_APPLY == train.code){
                        //ls业务代码
                        //1、解析字符串, 
                        //cmdStrArr[0] = "pwd", "ls", "cd"
                        //cmdStrArr[1] =              "dir1"
                        //cmdStrNum = 分割后字符串的个数

                        //2、查询数据库, //查询结果, 查询到了=1, 没查询到=2
                        //cUser[j].userName = 客户端登录的用户名
                        //cUser[j].clientPath = 遍历当前dirStack_t(pwd命令)
                        fileTableQuery(sqlconn, "file", cUser[j].userName, cUser[j].clientPath, &fileTable);
                        //3、返回结果
                        if(1 == fileTable.queryResult){
                            //查到了
                        }
                        else if(2 == fileTable.queryResult){
                            //没查到
                        }
                        else{
                            printf("mysql查询错误!\n");
                        }
                    }
                    else if(CD_APPLY == train.code){
                        //1、解析路径
                        printf("cmdStrArr[1] = %s\n", cmdStrArr[1]);
                        parseStr(cmdStrArr[1], cdStrArr, "/", &cdStrNum, &train);
                        for(int k = 0; k < cdStrNum; ++k){
                            printf("cdStrArr[%d] = %s\n", k, cdStrArr[k]);
                        }
                        //2、将路径替换为file表格式路径
                        bzero(cdTmpDir, 200);
                        tmpDirStack = dirStack[j];
                        dirRepalce(&tmpDirStack, cmdStrArr, cdStrNum, cdTmpDir);
                        printf("cdTmpDir = %s\n", cdTmpDir);
                        while(1);
                        //2、cUser[j].clientPath+cmdStrArr[1]为要去的目录
                        //在file表中查找, username = cUser[j].username, path=cUser[j].clientPath+cmdStrArr[1]
                        char tpath[200] = {0};
                        if(dirStack->top == -1){
                            sprintf(tpath, "%s%s%s", tpath, cUser[j].clientPath, cmdStrArr[1]);
                        }
                        else{
                            sprintf(tpath, "%s%s%s%s", tpath, cUser[j].clientPath, "/", cmdStrArr[1]);
                        }
                        fileTableQuery(sqlconn, "file", cUser[j].userName, tpath, &fileTable);
                        if(-1 == fileTable.queryResult){
                            //没查到，则不能进入该目录
                            train.code = CD_FAID;
                            memcpy(train.data, cmdStrArr[1], strlen(cmdStrArr[1]));
                            ret = send(cUser[j].netFd, &train, sizeof(train_t), MSG_NOSIGNAL);
                            ERROR_CHECK(ret, -1, "send");
                        }
                        else if(1 == fileTable.queryResult){
                            //有该目录，分析cd后面的字符串 cd [../dir, ./dir1, dir1]
                            //cmdStrArr[1] = [../dir, ./dir1, dir1]
                            
                        }
                        else{
                            printf("客户端输入参数错误\n");
                        }
                    }
                    else if(PWD_APPLY == train.code){
                        //pwd业务代码
                        //1、解析字符串, 
                        //cmdStrArr[0] = "pwd"
                        //cmdStrNum = 分割后字符串的个数 = 1

                        //2、获取当前路径, 遍历当前dirStack_t
                        achieveDir(&dirStack[j], cUser[j].clientPath);
                        printf("dirStack[%d].top = %d\n", j, dirStack[j].top);
                        printf("cUser[%d].clientPath = %s\n", j, cUser[j].clientPath);

                        //3、查询数据库, 查询结果, 查询到了=1, 没查询到=2
                        //username = cUser[j].userName = test;
                        //path = cUser[j].clientPath = "/"; 默认为"/"
                        //cUser[j].userName = 客户端登录的用户名
                        //cUser[j].clientPath = 遍历当前dirStack_t(pwd命令)
                        fileTableQuery(sqlconn, "file", cUser[j].userName, cUser[j].clientPath, &fileTable);
                        //3、返回结果
                        if(1 == fileTable.queryResult && 1 != fileTable.tomb){
                            //查到了, 把.和..加入
                            //sprintf(cUser[j].clientPath, "%s%s%s", cUser[j].clientPath, " .", " ..");
                            bzero(&train.data, strlen(train.data));
                            memcpy(train.data, cUser[j].clientPath, strlen(cUser[j].clientPath));
                            train.code = PWD_OK;
                            send(cUser[j].netFd, &train, sizeof(train_t), MSG_NOSIGNAL);
                        }
                        else{
                            //根目录
                            bzero(&train.data, strlen(train.data));
                            char rootDir[] = ". ..";
                            memcpy(train.data, rootDir, strlen(rootDir));
                            train.code = PWD_OK;
                            send(cUser[j].netFd, &train, sizeof(train_t), MSG_NOSIGNAL);
                        }
                    }
                    else if(PUTS_APPLY == train.code){}
                    else if(GETS_APPLY == train.code){}
                    else if(RM_APPLY == train.code){}
                    else if(MKDIR_APPLY == train.code){
                        printf("客户端命令为 %s\n", train.data);
                        printf("客户端命令参数个数为 %d\n", cmdStrNum);
                        printf("客户端命令为 %s %s\n", cmdStrArr[cmdStrNum-1], cmdStrArr[cmdStrNum-2]);
                        //1、先遍历目录获得path=当前pwd+train.data，查询mysql的file表，看文件夹是否存在，存在返回创建失败
                        char tmpStr[2048] = {0};
                        sprintf(tmpStr, "%s%s%s",tmpStr, cUser[j].clientPath, train.data);
                        printf("cUser[j].clientPath = %s\n", cUser[j].clientPath);
                        printf("tmpStr = %s\n", tmpStr);
                        fileTableQuery(sqlconn, "file", cUser[j].userName, tmpStr, &fileTable);
                        if(1 == fileTable.queryResult){
                            //存在该文件夹, 则不能创建，返回创建失败
                            train.code = MKDIR_FAID;
                            send(cUser[j].netFd, &train, sizeof(train_t), MSG_NOSIGNAL);
                        }
                        //2、不存在该文件夹, cUser[j].clientPath为当前目录路径, 根据它获取父目录的id, 将该文件夹插入file表
                        if(strcmp(cUser[j].clientPath, "/") == 0 && cmdStrNum == 2){
                            //根目录
                            bzero(&fileTable, sizeof(fileTable_t));
                            memcpy(fileTable.fileName, cmdStrArr[1], strlen(cmdStrArr[1]));
                            memcpy(fileTable.userName, cUser[j].userName, strlen(cUser[j].userName));
                            fileTable.pre_id = -1;//目录文件
                            memcpy(fileTable.path, "/", 1);
                            sprintf(fileTable.path, "%s%s", fileTable.path, cmdStrArr[1]);
                            memcpy(fileTable.type, "d", 1);
                            fileTable.fileMd5 = NULL;
                            fileTable.tomb = 0;//存在=0, 不存在=1
                            mysqlInsert(sqlconn, "file", &fileTable, &fileTable.queryResult);
                        }
                        else if(cmdStrNum == 2){
                            //取出栈顶元素,pre_id=栈顶元素的id, path=cUser[j].clientPath,再插入file表
                            bzero(&fileTable, sizeof(fileTable_t));
                            //获取pre_id
                            fileTableQuery(sqlconn, "file", cUser[j].userName, cUser[j].clientPath, &fileTable);
                            int pre_id = fileTable.id;
                            fileTable.pre_id = pre_id;

                            memcpy(fileTable.fileName, train.data, strlen(train.data));
                            memcpy(fileTable.userName, cUser[j].userName, strlen(cUser[j].userName));

                            char mkdirpath[200];
                            sprintf(mkdirpath, "%s%s%s", mkdirpath, "/", cUser[j].clientPath);
                            memcpy(fileTable.path, mkdirpath, strlen(mkdirpath));
                            
                            memcpy(fileTable.type, "d", 1);
                            fileTable.fileMd5 = NULL;
                            fileTable.tomb = 0;//存在=0, 不存在=1
                            mysqlInsert(sqlconn, "file", &fileTable, &fileTable.queryResult);
                        }
                        else{
                            printf("mkdir参数过少或过多\n");
                        }
                        
                        //3、给客户端返回创建文件夹成功
                        if(1 == fileTable.queryResult){
                            train.code = MKDIR_OK;
                            send(cUser[j].netFd, &train, sizeof(train_t), MSG_NOSIGNAL);
                        }
                        else{
                            train.code = MKDIR_FAID;
                            send(cUser[j].netFd, &train, sizeof(train_t), MSG_NOSIGNAL);
                        }
                    }
                    else if(RMDIR_APPLY == train.code){}
                    pthread_mutex_lock(&threadPool.mutex);
                    enQueue(&threadPool.taskQueue, cUser[j].netFd, train.code, &sqlconn, cUser[j].userName, cUser[j].userPasswd);
                    printf("I am master thread, I send a cUser[j].netFd = %d\n", cUser[j].netFd);
                    pthread_cond_signal(&threadPool.cond);
                    pthread_mutex_unlock(&threadPool.mutex);
                }
            }
        }
    }

    return 0;
}